package com.xiaoshuidi.cloud.module.finance.service.finance.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.module.contract.api.ContractRentOrderApi;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderPayStatusEnum;
import com.xiaoshuidi.cloud.module.contract.vo.RentOrderExtVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.flow.*;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.reconciliation.FinanceReconciliationFlowExcelVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.reconciliation.FinanceReconciliationPageResult;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountJournalDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceMoneyFlowDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceAccountJournalMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceMoneyFlowMapper;
import com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.finance.enums.finance.PaymentSourceEnum;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.finance.enums.order.FinanceCommonStatusEnum;
import com.xiaoshuidi.cloud.module.finance.mq.message.PayResultMessage;
import com.xiaoshuidi.cloud.module.finance.mq.producer.PayResultProducer;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceAccountService;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceMoneyFlowService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;

@Service
public class FinanceMoneyFlowServiceImpl implements FinanceMoneyFlowService {
    @Resource
    private FinanceMoneyFlowMapper financeMoneyFlowMapper;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Resource
    @Lazy
    private FinanceAccountService financeAccountService;

    @Resource
    private FinanceAccountJournalMapper financeAccountJournalMapper;

    @Resource
    private ContractRentOrderApi contractRentOrderApi;

    @Resource
    private PayResultProducer payResultProducer;

    @Override
    public FinanceMoneyFlowDO getById(Long id) {
        return financeMoneyFlowMapper.selectById(id);
    }

    @Override
    public PageResult<FinanceMoneyFlowDO> getFinanceMoneyFlowPage(FinanceMoneyFlowPageReqVO reqVO) {
        return financeMoneyFlowMapper.selectPage(reqVO);
    }

    @Override
    public PageResult<FinanceMoneyFlowDO> getConfirmFinanceMoneyFlowPage(FinanceMoneyFlowPageReqVO reqVO) {
        return financeMoneyFlowMapper.selectConfirmPage(reqVO);
    }

    @Override
    public List<FinanceMoneyFlowDO> getFinanceMoneyFlowListBySourceBizIdAndSourceBizType(String sourceBizType, Collection<String> sourceBizId) {
        QueryWrapper<FinanceMoneyFlowDO> query = Wrappers.query();
        query.eq("sourceBizType", sourceBizType);
        query.in("sourceBizId", sourceBizId);
        return financeMoneyFlowMapper.selectList(query);
    }

    @Override
    public String nextFlowNo() {
        return timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:finance:flow:no");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirm(Long id, FinanceMoneyFlowConfirmReqVO reqVO) {
        FinanceMoneyFlowDO financeMoneyFlowDO = financeMoneyFlowMapper.selectById(id);
        if (financeMoneyFlowDO == null || !PaymentSourceEnum.OFFLINE.name().equals(financeMoneyFlowDO.getSourceFrom())) {
            throw new ServiceException(ErrorCodeConstants.FINANCE_MONEY_FLOW_NOT_EXISTS);
        }
        if (FinanceCommonStatusEnum.WAITING.name().equals(financeMoneyFlowDO.getStatus())) {
            int c = financeMoneyFlowMapper.updateById(FinanceMoneyFlowDO.builder().id(financeMoneyFlowDO.getId())
                    .status(FinanceCommonStatusEnum.SUCCESS.name())
                    .payTime(LocalDateTime.now())
                    .attachment(reqVO.getAttachment())
                    //勾核人
                    .checkedBy(getLoginUser().getNickname())
                    .build());
            if (c > 0 && SourceBizTypeEnum.ACCOUNT_RECHARGE.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                financeAccountService.journalPayCallback(Long.parseLong(financeMoneyFlowDO.getSourceBizId()),
                        PayResultMessage.builder()
                                .success(true)
                                .payAmount(financeMoneyFlowDO.getTradeAmount().multiply(BigDecimal.valueOf(100)).longValue())
                                .payTime(new Date())
                                .build());
                //租金勾核
            } else if (c > 0 && SourceBizTypeEnum.RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                CommonResult<RentOrderExtVO> result =
                        contractRentOrderApi.getById(Long.parseLong(financeMoneyFlowDO.getSourceBizId()));
                //存在账单并且账单状态为待处理
                if (result.isSuccess() && ObjectUtil.isNotEmpty(result.getData())
                        && OrderPayStatusEnum.PROCESS.getCode().equals(result.getData().getPayStatus())) {
                    //发送消息
                    payResultProducer.sendPaySuccessMessage(financeMoneyFlowDO);
                }
                //企业账单勾核
            } else if (c > 0 && SourceBizTypeEnum.COMPANY_RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                //发送消息
                payResultProducer.sendPaySuccessMessage(financeMoneyFlowDO);
                //退租清算勾核
            } else if (c > 0 && SourceBizTypeEnum.SETTLEMENT_BILL.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                //发送消息
                payResultProducer.sendPaySuccessMessage(financeMoneyFlowDO);
            }
        }
    }

    /**
     * 流水勾核退回
     *
     * @param id
     * @param reqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void returnMoney(Long id, FinanceMoneyFlowConfirmReqVO reqVO) {
        FinanceMoneyFlowDO financeMoneyFlowDO = financeMoneyFlowMapper.selectById(id);
        if (financeMoneyFlowDO == null || !PaymentSourceEnum.OFFLINE.name().equals(financeMoneyFlowDO.getSourceFrom())) {
            throw new ServiceException(ErrorCodeConstants.FINANCE_MONEY_FLOW_NOT_EXISTS);
        }
        if (FinanceCommonStatusEnum.WAITING.name().equals(financeMoneyFlowDO.getStatus())) {
            int c = financeMoneyFlowMapper.updateById(FinanceMoneyFlowDO.builder().id(financeMoneyFlowDO.getId())
                    .status(FinanceCommonStatusEnum.FAILURE.name())
                    .attachment(reqVO.getAttachment())
                    //勾核人
                    .checkedBy(getLoginUser().getNickname())
                    //驳回原因
                    .rejectReason(reqVO.getRejectReason())
                    .build());
            if (c > 0 && SourceBizTypeEnum.ACCOUNT_RECHARGE.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                financeAccountJournalMapper.updateById(FinanceAccountJournalDO.builder()
                        .id(Long.parseLong(financeMoneyFlowDO.getSourceBizId()))
                        .status(FinanceCommonStatusEnum.FAILURE.name())
                        .build());
                //租金勾核
            } else if (c > 0 && SourceBizTypeEnum.RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                CommonResult<RentOrderExtVO> result =
                        contractRentOrderApi.getById(Long.parseLong(financeMoneyFlowDO.getSourceBizId()));
                //存在账单并且账单状态为待处理
                if (result.isSuccess() && ObjectUtil.isNotEmpty(result.getData())
                        && OrderPayStatusEnum.PROCESS.getCode().equals(result.getData().getPayStatus())) {
                    //修改账单状态为处理中
                    CommonResult<Boolean> booleanCommonResult =
                            contractRentOrderApi.updateOrderStatus(Long.parseLong(financeMoneyFlowDO.getSourceBizId()),
                                    result.getData().getOldPayStatus(), null);
                    if (!booleanCommonResult.isSuccess()) {
                        throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_TYPE_ERROR);
                    }
                }
            } else if (c > 0 && SourceBizTypeEnum.COMPANY_RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
                //回滚批企业账单
                CommonResult<Boolean> booleanCommonResult =
                        contractRentOrderApi.rollbackOrderBatchStatus(financeMoneyFlowDO.getSourceBizId());
                if (!booleanCommonResult.isSuccess()) {
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_TYPE_ERROR);
                }
            }

        }
    }

    @Override
    public FinanceMoneyFlowPageResult getFinanceMoneyFlowSummary(FinanceMoneyFlowPageReqVO reqVO) {
        List<Map<String, Object>> l = financeMoneyFlowMapper.selectSummary(reqVO);
        BigDecimal totalIncome = BigDecimal.ZERO;
        BigDecimal totalOutcome = BigDecimal.ZERO;
        for (Map<String, Object> m : l) {
            Object direction = m.get("direction");
            Object tradeAmount = m.get("tradeAmount");
            if (Boolean.FALSE.equals(direction)) {
                totalIncome = new BigDecimal(tradeAmount.toString());
            }
            if (Boolean.TRUE.equals(direction)) {
                totalOutcome = new BigDecimal(tradeAmount.toString());
            }
        }
        return FinanceMoneyFlowPageResult.builder().totalIncome(totalIncome)
                .totalOutcome(totalOutcome).build();
    }

    @Override
    public FinanceMoneyFlowConfirmPageResult getFinanceMoneyFlowConfirmSummary(FinanceMoneyFlowPageReqVO reqVO) {
        List<Map<String, Object>> l = financeMoneyFlowMapper.selectConfirmSummary(reqVO);
        BigDecimal confirmed = BigDecimal.ZERO;
        BigDecimal notConfirmed = BigDecimal.ZERO;
        for (Map<String, Object> m : l) {
            Object status = m.get("status");
            Object tradeAmount = m.get("tradeAmount");
            if (FinanceCommonStatusEnum.SUCCESS.name().equals(status)) {
                confirmed = new BigDecimal(tradeAmount.toString());
            }
            if (FinanceCommonStatusEnum.WAITING.name().equals(status)) {
                notConfirmed = new BigDecimal(tradeAmount.toString());
            }
        }
        return FinanceMoneyFlowConfirmPageResult.builder()
                .confirmed(confirmed)
                .notConfirmed(notConfirmed).build();
    }

    @Override
    public FinanceMoneyFlowDO getBySourceBizIdAndSourceBizType(String sourceBizId, String sourceBizType) {
        return financeMoneyFlowMapper.selectOne(new QueryWrapper<FinanceMoneyFlowDO>()
                .eq("source_biz_id", sourceBizId).eq("source_biz_type", sourceBizType)
                .orderByDesc("pay_time")
                .last("limit 1")
        );
    }

    @Override
    public List<FinanceMoneyFlowDO> getListBySourceBizIdAndSourceBizType(String sourceBizId, String sourceBizType) {
        return financeMoneyFlowMapper.selectList(new QueryWrapper<FinanceMoneyFlowDO>()
                .eq("source_biz_id", sourceBizId).eq("source_biz_type", sourceBizType)
                .orderByDesc("pay_time")
                .orderByDesc("id")
        );
    }

    @Override
    public FinanceReconciliationPageResult getReconciliationFlowSummary(FinanceMoneyFlowPageReqVO reqVO) {
        List<Map<String, Object>> l = financeMoneyFlowMapper.selectReconciliationSummary(reqVO);
        for (Map<String, Object> m : l) {
            return FinanceReconciliationPageResult.builder()
                    .totalCount(Integer.parseInt(m.get("totalCount").toString()))
                    .totalIncomeCount(Integer.parseInt(m.get("totalIncomeCount").toString()))
                    .totalPayoutCount(Integer.parseInt(m.get("totalPayoutCount").toString()))
                    .totalAlipayCount(Integer.parseInt(m.get("totalAlipayCount").toString()))
                    .totalWeixinCount(Integer.parseInt(m.get("totalWeixinCount").toString()))
                    .totalIncomeAmount(new BigDecimal(m.get("totalIncomeAmount").toString()))
                    .totalPayoutAmount(new BigDecimal(m.get("totalPayoutAmount").toString()))
                    .build();
        }
        return new FinanceReconciliationPageResult();
    }

    @Override
    public List<FinanceFlowMoneyVO> getFlow(String orderNo) {
        return financeMoneyFlowMapper.getFlowByOrderId(orderNo);
    }

    @Override
    public PageResult<FinanceMoneyFlowDO> pageFinanceMoneyFlow(FinanceMoneyFlowPageReqVO reqVO) {
        Page<FinanceMoneyFlowDO> page = new Page<>(reqVO.getPageNo(),reqVO.getPageSize());
        IPage<FinanceMoneyFlowDO> pageQueryList = financeMoneyFlowMapper.pageMoneyFlow(page, reqVO);
        if (CollectionUtils.isEmpty(pageQueryList.getRecords())){
            return new PageResult<>(new ArrayList<>(), page.getTotal());
        }
        return new PageResult<>(pageQueryList.getRecords(), page.getTotal());
    }

    @Override
    public List<FinanceReconciliationFlowExcelVO> getFinanceMoneyFlowExport(FinanceMoneyFlowPageReqVO reqVO) {
        return financeMoneyFlowMapper.getFlowExportList(reqVO);
    }
}
